Read Me About Son of Silly Balls

1.0d5

This sample is a port of a classic Mac OS sample, Silly Balls, whose sole purpose is to draw a bunch of randomly coloured, randomly placed, silly balls in a window on the screen.  While the actual code is trivial, porting Silly Balls to Yellow presented a number of interesting challenges which I describe in the ªCoding Notesº section.

User Level Operation

The compiled version of this sample was built for a pre-Developer release of Rhapsody (ie OpenStep/Mach 4.2 for Intel + Yellow 1K). Your mileage may vary.

To use the program, simple launch the SonOfSillyBalls.app and watch your screen fill up with silly balls.  There is a slider to alter the rate at which balls are drawn, and a few menu items to start, stop and clear the display.

Building the Code

The code was built for Intel on pre-Developer release of Rhapsody (ie OpenStep/Mach 4.2 for Intel + Yellow 1K). Your mileage may vary.

The code uses the new graphics APIs that were added to Rhapsody post OpenStep 4.2. Therefore you will not be able to compile it on pre-Rhapsody systems.

To build the code, launch the ªPB.projectº file inside the ªSonOfSillyBallsº folder. After the project opens, choose ªBuildº from the ªProject Buildº submenu of the ªToolsº menu. When the build finishes, choose ªRunº from the ªLauncherº submenu of the ªToolsº menu to execute the code.

Code Structure

The sample contains only one class, SillyBallView.  This is a subclass of NSView that repeatably draws randomly coloured balls at random positions inside its view rectangle.

Restrictions

The sample embodies some restrictions that are inherent in its design:

·	Localised strings Ð The strings in Silly Balls are not localisable.  I left this out to make the code simpler and, besides, it would contradict the spirit of this sample: they weren't localisable on the Mac OS version either (-:

Coding Notes

This section contains notes about why and how the code was written the way it was.

Monopolising the CPU

Silly Balls for Mac OS does not yield the CPU to other processes; it just opens up a window and draws into it until you click the mouse button.  This sort of behaviour is obviously not suitable for the Yellow application environment, so I had to figure out a more appropriate approach.

The approach I used to was to use a timer to draw the balls.  Using the Foundation class NSTimer, you can create a timer that calls a specific method in a specific object on a periodic basis.  So Silly Balls sets up a timer to call the SillyBallView's drawAnother: method on a periodic basis.  Each time that method is called it draws another silly ball.  When you drag the slider to set the ball redraw period, I invalidate the repeat timer and create a new one with the correct period.

Drawing

The Mac OS provides a very direct drawing model suitable for tasks such as Silly Balls.  You just open up a window, set the port to it, and draw into it.  There is very little to get in the way.

In contrast, the OpenStep does not allow you to just open up a window and draw.  So I created a new subclass of NSView (the base class for all drawing and event handling objects) and used it to draw my silly balls.  Normally you would do the drawing in a custom view by overriding NSView's drawRect: method.  The system calls your drawRect: method when your area of the screen needs updating.  But silly balls are draw dynamically, and Silly Balls does not remember the position of each of the balls that its drawn, so there's no way it could redraw them.  Also, the balls are drawn dynamically, at a furious rate. Overriding drawRect: is not enough for this view.

Instead of doing my drawing in the drawRect: method, I actually do my drawing in a method that is repeatedly called by a timer (SillyBallView's drawAnother: method).  Because I'm drawing outside of an NSView's drawRect: method, I have to explicitly lock the drawing focus on my view before drawing into it, and unlock the focus afterwards.  You can see the code in drawAnother:.  This is a significant digression from the standard approach to updating a view.

Note: Locking focus on a view is like calling SetPort on a GrafPort under Mac OS: it sets up the view for you to draw into it.  The drawRect: method is automatically called with the focus locked on the view.

This all worked, but I found that none of my balls were actually showing up on the screen until I modified some other parts of the window's contents.  I quickly tracked this down to the window backing model.  By default under Yellow, the contents of each window is buffered offscreen so that all drawing goes into the offscreen buffer and is copied on to the screen when all the drawing is done.  This typically results in very clean window updates, but causes problems for a silly application like Silly Balls.

To get around this, I call the window's flushWindow method immediate after I draw a ball.  This causes the window's backing buffer to me immediately flushed to the screen, and the balls show up in a timely fashion.

Ovals et al

Traditionally under OpenStep, anyone doing sophisticated drawing would do using direct Display PostScript calls. So doing something that's very easy on Mac OS, like drawing an oval, required you to know some basic PostScript concepts.

Rhapsody introduces a new graphics API that allows you to do advanced drawing without requiring Display PostScript.  Son of Silly Balls uses this new API to draw the balls.  To see the code, take a look at SillyBallView's drawRandomBallInside: method.  You can learn more about the graphics API by reading the documentation for the NSBezielPath, NSAffineTransform and NSColor classes.

Another area that challenged me was drawing text.  The silly balls drawn by this sample have text inside them, but it wasn't easy finding a way to draw a single line of text.  [I considered building an NSTextField, but thought that would be overkill.]  Just as I was about to give up and draw the text using PostScript (which I've since learnt is a very bad idea), I found a category in App Kit's ªNSStringDrawing.hº that adds text drawing methods to the NSString class.

Credits and Version History

Version 1.0d1 was distributed for review inside DTS.

Version 1.0d2 was distributed for review inside Apple.  It includes some documentation and minor comment changes.

Version 1.0d3 is the first widely released version.  The window is now buffered instead of non-retained, and flushWindow is used to force the balls to appear immediately. Drawing is no longer done using PostScript but with the new-in-Rhapsody graphics API (NSBezierPath). The documentation changed to cover the above.

Version 1.0d4 added code to make sure that the main window is active after launch [Radar ID 1683474].

Version 1.0d5 got some HI modifications (DG).

Share and Enjoy.

Quinn ªThe Eskimo!º
Apple Developer Technical Support
Rhapsody Networking and Low-Level Stuff

8 October 1997